/**
 * 
 */
package org.statewalker.parser.xhtml.serializer;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Stack;

import org.statewalker.dict.BaseDict;
import org.statewalker.dict.ITextToken;
import org.statewalker.dict.IToken;
import org.statewalker.dict.IXmlToken;
import org.statewalker.parser.xhtml.grammar.XHTMLGrammar;
import org.statewalker.parser.xhtml.validator.XHTMLValidatorFactory;
import org.statewalker.parser.xhtml.validator.XHTMLValidator.IExtraTokenListener;
import org.statewalker.tokenizer.text.TextDict;
import org.statewalker.tokenizer.xml.XMLDict;
import org.statewalker.validator.IValidatorListener;
import org.statewalker.validator.ValidatorState;

/**
 * Translates XHTML tokens to calls of the {@link IXmlListener}.
 * 
 * @author kotelnikov
 */
public class XhtmlListener implements IValidatorListener, IExtraTokenListener {

    protected StringBuilder fBuf = new StringBuilder();

    private IXmlListener fListener;

    private Stack<ValidatorState> fStack = new Stack<ValidatorState>();

    private XHTMLGrammar XHTML_GRAMMAR;

    public XhtmlListener(XHTMLValidatorFactory factory, IXmlListener listener) {
        XHTML_GRAMMAR = factory.getXhtmlGrammar();
        fListener = listener;
    }

    protected boolean checkState(ValidatorState state, IToken event) {
        String key = state.getKey();
        if (BaseDict._ALL_SET.contains(key))
            return false;
        return true;
    }

    private void clear() {
        fBuf.delete(0, fBuf.length());
    }

    private void flush() {
        if (fBuf.length() > 0) {
            fListener.onText(fBuf.toString());
        }
        clear();
    }

    protected Map<String, String> getAttributes(ValidatorState state) {
        IToken stateToken = state.getToken();
        if (!(stateToken instanceof IXmlToken))
            return null;
        IXmlToken xmlToken = (IXmlToken) stateToken;
        Map<String, String> attrs = xmlToken.getAttributes();
        return attrs;
    }

    public void onActivate(
        ValidatorState parent,
        ValidatorState state,
        IToken event) {
        String key = state.getKey();
        ValidatorState result = null;
        if (TextDict.EOL.equals(key)
            || TextDict.SPACES.equals(key)
            || TextDict.WORD.equals(key)
            || TextDict.SPECIAL.equals(key)) {
            ITextToken token = (ITextToken) event;
            String s = token.getContent();
            print(s);
        } else if (XMLDict.CDATA.equals(key)) {
            ITextToken token = (ITextToken) event;
            fListener.onCDATA(token.getContent());
            clear();
        } else if (XMLDict.ENTITY.equals(key)) {
            flush();
            ITextToken token = (ITextToken) event;
            print(token.getContent());
        } else {
            if (checkState(state, event)) {
                flush();
                Map<String, String> attributes = null;
                if (key.equals(event.getKey())) {
                    attributes = getAttributes(state);
                }
                if (attributes == null) {
                    attributes = new LinkedHashMap<String, String>();
                }
                // IToken stateToken = state.getToken();
                // FIXME
                String tagName = key;
                if (XHTML_GRAMMAR.EMPTY_TAGS.contains(key)) {
                    fListener.onEmptyTag(tagName, attributes);
                } else {
                    fListener.beginTag(tagName, attributes);
                    result = state;
                }
            }
        }
        fStack.push(result);
    }

    public void onDeactivate(
        ValidatorState parent,
        ValidatorState state,
        IToken event) {
        ValidatorState currentState = fStack.pop();
        if (currentState != null) {
            flush();
            String tagName = currentState.getKey();
            fListener.endTag(tagName);
        }
    }

    public void onToken(ValidatorState state, IToken token) {
        String key = token.getKey();
        if (TextDict.SPACES.equals(key) || TextDict.EOL.equals(key)) {
            flush();
            ITextToken t = (ITextToken) token;
            String str = t.getContent();
            fListener.onExtraSpaces(str);
        } else if (XMLDict.COMMENT.equals(key)) {
            flush();
            ITextToken t = (ITextToken) token;
            String str = t.getContent();
            if (str.startsWith("<!--")) {
                str = str.substring("<!--".length());
                if (str.startsWith(" "))
                    str = str.substring(1);
            }
            if (str.endsWith("-->")) {
                str = str.substring(0, str.length() - "-->".length());
                if (str.endsWith(" ")) {
                    str = str.substring(0, str.length() - 1);
                }
            }
            fListener.onComment(str);
        }
    }

    public void onTransition(
        ValidatorState parent,
        ValidatorState prev,
        IToken event,
        ValidatorState next) {
    }

    protected final void print(String s) {
        fBuf.append(s);
    }

}